const {
    GoogleGenerativeAI,
    HarmCategory,
    HarmBlockThreshold,
} = require("@google/generative-ai");

// const Datastore = require('nedb');

class Gemini {
    constructor(defaultModelConfig){
        this._modelConfig = defaultModelConfig;
        this._clientArr = {};
    }

    async createClient(modelConfig, generationConfig, safetySettings) {
        return GeminiCore.createInstance(modelConfig ?? this._modelConfig, generationConfig, safetySettings);
    }

    async getReplyWithId(user_id, prompt, history_en, modelConfig, generationConfig, safetySettings) {
        if(this._clientArr[user_id]) return this._clientArr[user_id].getReply(prompt);
        return new Promise((resolve, reject)=>{
            this.createClient(modelConfig, generationConfig, safetySettings)
                .then(res => {
                    // console.log(res)
                    this._clientArr[user_id] = res;
                    this._clientArr[user_id].getReply(prompt)
                        .then(ret => resolve(ret))
                        .catch(err => reject(err));
                })
                .catch(err => reject(err));
        });
    }

    async getReplyStreamWithId(user_id, prompt, history_en, modelConfig, generationConfig, safetySettings) {
        if(this._clientArr[user_id]) return this._clientArr[user_id].getReplyStream(prompt);
        return new Promise((resolve, reject)=>{
            this.createClient(modelConfig, generationConfig, safetySettings)
                .then(res => {
                    // console.log(res)
                    this._clientArr[user_id] = res;
                    this._clientArr[user_id].getReplyStream(prompt)
                        .then(ret => resolve(ret))
                        .catch(err => reject(err));
                })
                .catch(err => reject(err));
        });
    }

    async resetHistoryById(user_id) {
        if(!user_id) return Promise.reject(new Error(`NULL PARAMS`));
        const index = this._clientArr.indexOf(user_id);
        if(index === -1) return Promise.reject(new Error(`NO HISTORY`));
        return new Promise(resolve => {
            this._clientArr.splice(index, 1);
            resolve();
        })
    }

}

class GeminiCore {

    static _generationConfig = {
        temperature: 1,
        topK: 64,
        topP: 0.95,
        maxOutputTokens: 3072,
    };
    static _safetySettings = [
        {
            category: HarmCategory.HARM_CATEGORY_HARASSMENT,
            threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
        },
        {
            category: HarmCategory.HARM_CATEGORY_HATE_SPEECH,
            threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
        },
        {
            category: HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,
            threshold: HarmBlockThreshold.BLOCK_NONE,
        },
        {
            category: HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
            threshold: HarmBlockThreshold.BLOCK_MEDIUM_AND_ABOVE,
        },
    ];

    static async createInstance(modelConfig, generationConfig, safetySettings) {
        try {
            const genAI = new GoogleGenerativeAI(modelConfig.API_KEY);
            const model = genAI.getGenerativeModel({ model: modelConfig.MODEL_NAME ?? "gemini-1.5-pro-latest" });
            const chat = model.startChat({
                generationConfig: generationConfig ?? this._generationConfig,
                safetySettings: safetySettings ?? this._safetySettings,
            });
            return new GeminiCore(chat, model);
        } catch (err) { throw err }

    }
    constructor(chat, model) {
        this._chat = chat;
        this._model = model;
        this._replying = false;
    }

    async getReply(prompt) {
        if(this._replying) return Promise.reject("Replying");
        this._replying = true;
        return new Promise((resolve, reject) => {
            this._chat.sendMessage(prompt)
                .then(res => {
                    if (res && res.response && res.response.text) resolve(res.response.text())
                    else reject("Illegal response.")
                    this._replying = false;
                })
                .catch(err => {
                    this._replying = false;
                    reject(err)
                })
        })
    }

    async getReplyStream(prompt) {
        if(this._replying) return Promise.reject("Replying");
        this._replying = true;
        let ret = await this._model.generateContentStream(prompt);
        this._replying = false;
        return Promise.resolve(ret.stream);
    }

    async getHistory() {

    }
}


// class DataBase {

//     constructor(path) {
//         this._db = new Datastore({
//             autoload: true,
//             filename: path ?? './res/userData'
//         });
//     }

//     async getHistoryByUserId(user_id) {
//         return new Promise((resolve, reject) => {
//             this._db.find({ user_id }, (err, res) => {
//                 if (err) { reject("DB_Find Err."); return }
//                 if (!res) { reject("DB_Find Null Data."); return }
//                 res = res.map(item => {
//                     return {
//                         user_id: item.user_id,
//                         role: item.role,
//                         text: item.text,
//                     }
//                 });
//                 resolve(res);
//             })
//         })
//     }

//     async pushHistory(user_id, sentence) {
//         return new Promise((resolve, reject) => {
//             if (!user_id || !sentence || !sentence.role || !sentence.text) {
//                 reject("Null input.");
//                 return;
//             }
//             this._db.insert(
//                 {
//                     user_id,
//                     role: sentence.role,
//                     text: sentence.text
//                 },
//                 err => {
//                     if (err) {
//                         reject("DB_Insert Err.");
//                         return;
//                     }
//                     resolve()
//                 }
//             )
//         })
//     }

//     async clearHistoryById(user_id) {
//         return new Promise((resolve, reject) => {
//             if (!user_id) { reject("Null input."); return };
//             this._db.remove({ user_id }, { multi: true }, (err, numRemoved) => {
//                 if (err) {
//                     console.log(err);
//                     reject("DB_Remove Err.");
//                     return;
//                 }
//                 resolve(numRemoved);
//             })
//         })
//     }
// }

module.exports = Gemini;
